import base64
import json
import time
import requests
import urllib3

urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

ip = '127.0.0.1'
username = "Administrator"
password = "Admin@90000"
auth = (username, password)
port = 10443

def get_auth_header():
    encode_info = base64.b64encode('{}:{}'.format(
        username, password).encode('utf-8')).decode('utf-8')
    return {"Authorization": "Basic {}".format(encode_info)}

def do_get(uri):
    req_url = 'https://{}:{}{}'.format(ip, port, uri)
    try:
        rsp = requests.get(
            url=req_url, headers=get_auth_header(), auth=auth, verify=False, timeout=(2, 30))
        return rsp
    except requests.exceptions.RequestException as e:
        print(e)
        return None
    
def do_post(uri, body):
    req_url = 'https://{}:{}{}'.format(ip, port, uri)
    try:
        rsp = requests.post(
            url=req_url, headers=get_auth_header(), json={
                "ResetType": "ForceRestart"
            }, auth=auth, verify=False, timeout=(2, 30))
        return rsp
    except requests.exceptions.RequestException as e:
        print(f'do_post error: {e}')
        return None
    
#启动性能测试步骤：
# 1、获取板卡集合资源和SystemOverview资源
# 2、重启BMC
# 3、监控BMC重启完成时间，以redfish接口访问出现网络错误作为启动时间计数起点
# 4、再次访问板卡资源，直到访问成功且内容与重启前一致后，访问SystemOverview资源，直到与重启前内容一致后结束，以此时间作为启动时间计数终点
# 它这个就是启动时间啊

def get_rsc(uri):
    # 先获取资源槽位号
    rsp = do_get(uri)
    if rsp == None or rsp.status_code != 200:
        raise ValueError("GET类型请求返回异常")
    
    body = json.loads(rsp.text)
    return body

def is_board_src_same(origin, current):
    if 'Members' not in origin or 'Members' not in current:
        return False
    
    if origin['Members@odata.count'] != current['Members@odata.count']:
        return False
    
    # 从json对象中提取板卡名称
    def extract_board_names(json_obj):
        return set(member['@odata.id'].split('/')[-1] for member in json_obj['Members'])

    print(f"原板卡资源数据为{extract_board_names(origin)}")
    print(f"现板卡资源数据为{extract_board_names(current)}")

    return extract_board_names(origin) == extract_board_names(current)

def is_system_overview_src_same(origin, current):
    # print(f"is_system_overview_src_same origin: {origin}")
    # print(f"is_system_overview_src_same current: {current}")
    
    if type(origin) != type(current):
        return False
    
    if isinstance(origin, dict):
        if set(origin.keys()) != set(current.keys()):
            return False
        for key in origin:
            if key == 'DateTime' or key == 'PermanentMACAddress' or key == 'DeviceIPv6' or key == 'PowerState':
                if key in current:
                    continue
                else:
                    return False
            if not is_system_overview_src_same(origin[key], current[key]):
                return False
    elif isinstance(origin, list):
        if len(origin) != len(current):
            return False
        for item1, item2 in zip(origin, current):
            if not is_system_overview_src_same(item1, item2):
                return False
    elif isinstance(origin, (str, bool)):
        if origin != current:
            return False
    elif isinstance(origin, (int, float)):
        pass
    elif origin is None:
        pass
    else:
        return False
    
    return True
    

def monitor_bmc_restart(board_collection_data, system_overview_data):
    start_time = None
    restart_time = None
    while restart_time is None or restart_time < 10 * 60:
        try:
            # 尝试获取当前的板卡集合资源
            current_board_collection_data = get_rsc('/redfish/v1/Chassis/1/Boards')
            current_system_overview_data = get_rsc('/redfish/v1/SystemOverview')
            # 如果BMC重启前后的数据一致，且start_time已经记录，则计算并打印重启时间
            if start_time is not None and is_board_src_same(board_collection_data, current_board_collection_data) is True and is_system_overview_src_same(system_overview_data, current_system_overview_data) is True:
                end_time = time.time()
                print(f"BMC restart completed in {end_time - start_time} seconds.")
                break
        except ValueError as e:
            # 网络错误，如果start_time未记录，则记录当前时间作为start_time
            print(e)
            if start_time is None:
                start_time = time.time()
                print(f"开启启动重启bmc计时，开始时间为 {start_time}")
                print('再次访问板卡集合资源，直到访问成功且内容与重启前一致后，访问SystemOverview资源，直到与重启前内容一致后结束，以此时间作为启动时间计数终点')
            pass
        if start_time is not None:
            restart_time = time.time() - start_time
        time.sleep(0.5)
        
def main():
    # 1 获取板卡集合资源和SystemOverview资源
    print('获取板卡集合资源和SystemOverview资源')
    boards_rsc_origin = get_rsc('/redfish/v1/Chassis/1/Boards')
    systemoverview_rsc_origin = get_rsc('/redfish/v1/SystemOverview')
    
    # 2 重启BMC
    print('重启BMC')
    do_post('/redfish/v1/Managers/1/Actions/Manager.Reset', {
        "ResetType": "ForceRestart"
    })
    
    # 3 监控BMC重启完成时间，以redfish接口访问出现网络错误作为启动时间计数起点
    # 4 再次访问板卡集合资源，直到访问成功且内容与重启前一致后，访问SystemOverview资源，直到与重启前内容一致后结束，以此时间作为启动时间计数终点
    print("监控BMC重启完成时间，以redfish接口访问出现网络错误作为启动时间计数起点")
    monitor_bmc_restart(boards_rsc_origin, systemoverview_rsc_origin)
    
if __name__ == '__main__':
    main()




